package com.uwo.commons.configuration.aop.validate;

import com.uwo.commons.configuration.aop.validate.annotation.Model;
import com.uwo.commons.configuration.aop.validate.annotation.ValidateMethod;
import com.uwo.commons.configuration.aop.validate.excepteion.ApiValidateExceptrion;
import com.uwo.commons.configuration.aop.validate.excepteion.CommonResponse;
import com.uwo.commons.kits.ClassKits;
import com.uwo.commons.kits.JsonKits;
import com.uwo.commons.kits.StringKits;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;

import javax.annotation.PostConstruct;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * 切面校验参数
 * @author hao.yan
 */
@Configuration
@Aspect
public class ValidateAspect {

    @Autowired
    private ValidateProperties validateProperties;

    private final Logger log = LoggerFactory.getLogger(ValidateAspect.class);

    @Autowired
    private ApplicationContext applicationContext;

    @Autowired(required = false)
    private CommonResponse commonResponse;

//    /**
//     * 添加切面
//     * "@annotation(com.uwo.commons.configuration.aop.validate.annotation.ValidateMethod)"
//     */
//    @Pointcut("@annotation(com.uwo.commons.configuration.aop.validate.annotation.ValidateMethod)")
//    public void aspectMethod(){}

    /**
     * 校验参数
     * @param point
     * @param validateMethod
     * @return
     * @throws Throwable
     */
    @Around(value = "@annotation(validateMethod)")
    public Object around(ProceedingJoinPoint point, ValidateMethod validateMethod) throws Throwable{
        // 获取方法
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        try {
            // 获取请求的参数名
            LocalVariableTableParameterNameDiscoverer localVariableTableParameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
            String[] paraNameArr = localVariableTableParameterNameDiscoverer.getParameterNames(method);
            // 所有的参数注解
            Annotation[][] annotations = method.getParameterAnnotations();
            // 所有的参数类型
            Class<?>[] parameterTypes = method.getParameterTypes();
            // 所有的参数值
            Object[] parameterValues = point.getArgs();
            // 验证字段
            for (int i = 0; i < parameterTypes.length; i ++){
                // 类型是接口结束
                if(parameterTypes[i].isInterface())
                    continue;
                validate(annotations[i],
                    createValidateField(
                            paraNameArr[i],
                        parameterTypes[i],
                        parameterValues[i]
                    )
                );
            }

            Object o = point.proceed(point.getArgs());
            log.info("{}.{}(..), response result = {}", point.getTarget().getClass(), method.getName(), JsonKits.object2Json(o));
            return o;
        }catch (ApiValidateExceptrion e){
            // 返回对象
            Object o = commonResponse(validateMethod).responseHandler(e.getCode(), e.getMessage(), point.getArgs());
            log.error("{}.{}(..), response result = {}", point.getTarget().getClass(), method.getName(),JsonKits.object2Json(o));
            // 判断返回类型是否是void
            if(ClassKits.isVoid(method.getReturnType()))
                throw e;
            return o;
        }
    }

    /**
     * 先查看注解中的bean在拿取全局中的bean
     * @param validateMethod
     * @return
     */
    private CommonResponse commonResponse(ValidateMethod validateMethod){
        if(StringKits.isEmpty(validateMethod.responseBean()))
            return this.commonResponse;
        if(!applicationContext.containsBean(validateMethod.responseBean()))
            return this.commonResponse;
        return (CommonResponse)applicationContext.getBean(validateMethod.responseBean());
    }

    /**
     * 校验
     * @param annotations
     * @param validateField
     * @throws ApiValidateExceptrion
     */
    private void validate(Annotation[] annotations, ValidateField validateField)throws ApiValidateExceptrion{
        // 字段校验
        validateField(annotations, validateField);

        // 模型校验
        validateModel(validateField);
    }

    /**
     * 模型校验
     * @param validateField
     * @throws ApiValidateExceptrion
     */
    private void validateModel(ValidateField validateField) throws ApiValidateExceptrion{
        // 判断类是接口结束校验
        if(ClassKits.isInterface(validateField.getType()))
            return;

        // 判断是基础类型结束校验
        if (ClassKits.isTypeClass(validateField.getType()))
            return;

        // 判断是集合类型结束校验
        if (ClassKits.isCollection(validateField.getValue()))
            return;

        // 判断值为空结束校验
        if(null == validateField.getValue())
            return;

        // 父类模型校验
        validateModel(
            new ValidateField(
                validateField.getName(),
                validateField.getType().getSuperclass(),
                validateField.getValue()
            )
        );

        // 模型字段校验
        validateFields(validateField.getType(), validateField.getValue());

        // 模型方法校验
        validateMethod(validateField.getType(), validateField.getValue());
    }

    /**
     * 方法验证模型
     * @param clazz
     * @param model
     * @throws ApiValidateExceptrion
     */
    private void validateMethod(Class<?> clazz, Object model) throws ApiValidateExceptrion{
        try{
            // 是存在模型验证
            if(!clazz.isAnnotationPresent(Model.class)){
                return;
            }
            Model validateModel = clazz.getAnnotation(Model.class);
            String beanName = validateModel.validateBean();
            //
            if(StringKits.isEmpty(beanName))
                return;
            if(!this.applicationContext.containsBean(beanName))
                return;
            Object validateBean = this.applicationContext.getBean(beanName);
            if(validateBean == null)
                return;
            Method method = validateBean.getClass().getDeclaredMethod(validateModel.method(), clazz);
            method.invoke(validateBean, model);
        } catch (Exception e) {
            throw new ApiValidateExceptrion(ErrorCode.SYSTEM_ERROR);
        }
    }

    /**
     * 验证类中的字段
     * @param clazz
     * @param model
     * @throws ApiValidateExceptrion
     */
    private void validateFields(Class<?> clazz, Object model) throws ApiValidateExceptrion {
        if(null == clazz)
            return;
        Field[] fields = clazz.getDeclaredFields();
        for (Field field:fields){
            // 获取值
            Object value = ClassKits.getProperty(field, model);
            // 创建校验字段
            ValidateField validateField = createValidateField(field.getName(), field.getType(), value);
            // 字段校验
            validateField(field.getDeclaredAnnotations(), validateField );
            // 模型校验
            validateModel(validateField);
        }
    }

    /**
     * 字段验证
     * @param annotations
     * @param validateField
     * @throws ApiValidateExceptrion
     */
    private void validateField(Annotation[] annotations, ValidateField validateField) throws ApiValidateExceptrion {
        for (Annotation annotation:annotations) {
            String validateName = "Validate_" + annotation.annotationType().getSimpleName();
            if(!this.applicationContext.containsBean(validateName))
                continue;
            Object validateBean = this.applicationContext.getBean(validateName);
            if (validateBean == null)
                return;
            Validate validate = (Validate) validateBean;
            validate.validate(annotation, validateField);
        }
    }

    /**
     * 创建校验字段
     * @param name
     * @param type
     * @param value
     * @return
     */
    private ValidateField createValidateField(String name, Class<?> type, Object value){
        type = (null == value) ? type : value.getClass();
        return new ValidateField(name, type, value);
    }

}
